package com.fingard.remoting;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Date;

import com.fingard.FGBiz;
import com.fingard.diagnostics.LogHelper;
import com.fingard.dsp.bank.reqrespobj.ReqRespHeader;
import com.fingard.dsp.tcpserver.SocketReceiver;
import com.fingard.net.ConnState;
import com.fingard.text.StringHelper;

public class TenantSocket {
	private String ip = "";
	private int port = 0;
	private String charset = "";
	Socket socket = null;
	
	public String connState;
	public String connMsg;
	
	public String tenant;
	
	public TenantSocket(String p_ip, int p_port, String pCharset, String pTenant){
		ip = p_ip;
		port = p_port;
		charset = pCharset;
		if(StringHelper.isNullOrEmpty(pCharset)){
			charset = "gbk";
		}
		tenant = pTenant;
		isConnected = false;
	}
	
	public boolean isConnected;
	
	private OutputStream writeStream;//发
	private InputStream readStream;//读
	public Date lastRecDate;
	
	public boolean connect(){
		try {
			connState = "";
			connMsg = "";
			socket = new Socket(ip, port);
			socket.setSoTimeout(10000);
			
			writeStream = socket.getOutputStream();
			String reqStr = "tenant: "+tenant+"\r\n"+ "cipher-key: ckey\r\n"+"cipher-iv: civ";
			byte[] tmpBytesBody = reqStr.getBytes(charset);
	        String tmpStrLen = String.format("% 8d", tmpBytesBody.length);

	        String tmpSendHead = "tenant-connect:" + tmpStrLen + "\r\n\r\n";
	        byte[] tmpBytesHead = tmpSendHead.getBytes(charset);

	        byte[] tmpBytesSend = new byte[tmpBytesHead.length + tmpBytesBody.length];
	        System.arraycopy(tmpBytesHead, 0, tmpBytesSend, 0, tmpBytesHead.length);
	        System.arraycopy(tmpBytesBody, 0, tmpBytesSend, tmpBytesHead.length, tmpBytesBody.length);
	        
	        writeStream.write(tmpBytesSend);
			readStream = socket.getInputStream();
			
			ReqRespHeader bankHeader = new ReqRespHeader();
			SocketReceiver retReceiver = receiveBytes(bankHeader);
			
			if(retReceiver.isCompleted){
				isConnected = true;
				FGBiz.limitMsg.addConsole("TenantSocket.connect", "连接成功");
			}else{
				isConnected = false;
				FGBiz.limitMsg.error("TenantSocket.connect", retReceiver.errMsg);
				readStream.close();
				writeStream.close();
				socket.close();
			}
			
		} catch (Exception e) {
			isConnected = false;
			connState = ConnState.FAIL;
			connMsg = e.getMessage();
			FGBiz.limitMsg.exception("TenantSocket.connect", e, ip+":"+port);
			disconnect();
		}
		return isConnected;
	}
	
	/**接收字节*/
    public SocketReceiver receiveBytes(ReqRespHeader bankHeader){
    	SocketReceiver reqSocketReceiver = new SocketReceiver(socket, charset);
    	if(!isConnected){
    		reqSocketReceiver.hasError = true;
    		reqSocketReceiver.errMsg = "未连接";
    	}
		try {
			reqSocketReceiver.reqBytes = new byte[2048];//2K
			while(!reqSocketReceiver.isCompleted || !reqSocketReceiver.hasError){
		    	int tmpReadLen = readStream.read(reqSocketReceiver.reqBytes, reqSocketReceiver.receiveOffSet, reqSocketReceiver.reqBytes.length - reqSocketReceiver.receiveOffSet);
				if (tmpReadLen <= 0) {
					reqSocketReceiver.syncCode = "-1";
                	int expectLen = reqSocketReceiver.contentLength + reqSocketReceiver.contentStartIndex;
                	int actualLen = reqSocketReceiver.readedLength-reqSocketReceiver.contentStartIndex;
                	reqSocketReceiver.syncMsg = "接收异常，预期总字节数"+expectLen+"，已接收"+reqSocketReceiver.readedLength+"，其中已接收内容字节数"+actualLen+"，预期内容字节数"+reqSocketReceiver.contentLength;
                	reqSocketReceiver.hasError = true;
                	reqSocketReceiver.errMsg = reqSocketReceiver.syncMsg;
                	return reqSocketReceiver;
		        }else{
		        	lastRecDate = new Date();
		        }
				
				reqSocketReceiver.readedLength += tmpReadLen;
				reqSocketReceiver.receiveOffSet += tmpReadLen;
				
				reqSocketReceiver.getHeader(bankHeader);
	            
	            if (reqSocketReceiver.contentLength < 0 || reqSocketReceiver.contentStartIndex < 0){
	                if (reqSocketReceiver.isCipherSocket == false && reqSocketReceiver.isSyncFile == false && reqSocketReceiver.receiveOffSet > 200){
	                	reqSocketReceiver.errMsg = "未知的通讯头部：" + new String(reqSocketReceiver.reqBytes, 0, reqSocketReceiver.receiveOffSet, reqSocketReceiver.charsetName);
	                	reqSocketReceiver.contentStartIndex = -1;
	                	reqSocketReceiver.hasError = true;
	                	return reqSocketReceiver;
	                }
	            }
	            
	            if (reqSocketReceiver.contentLength > 0 && reqSocketReceiver.contentStartIndex > 0) {
	                if(reqSocketReceiver.isSyncFile){//文件同步
	                	if(StringHelper.isNullOrEmpty(reqSocketReceiver.oriFilePath)){
	                		reqSocketReceiver.syncCode = "-1";
	                		reqSocketReceiver.syncMsg = "文件传输无文件路径";
	                		reqSocketReceiver.hasError = true;
	                		reqSocketReceiver.errMsg = reqSocketReceiver.syncMsg;
	                        return reqSocketReceiver;
	                	}
	                	if(reqSocketReceiver.saveFile == null){//第一次写入
	                		reqSocketReceiver.openSaveFile(bankHeader);
	                		if((reqSocketReceiver.receiveOffSet-reqSocketReceiver.contentStartIndex) > 0){
	                			reqSocketReceiver.saveFileStream.write(reqSocketReceiver.reqBytes, reqSocketReceiver.contentStartIndex, reqSocketReceiver.receiveOffSet-reqSocketReceiver.contentStartIndex);
	                    		if(reqSocketReceiver.contentLength>102400){//扩大缓冲区
	                    			reqSocketReceiver.reqBytes = new byte[51200];//50K
	                    		}else if(reqSocketReceiver.contentLength>40960){//扩大缓冲区
	                    			reqSocketReceiver.reqBytes = new byte[20480];//20K
	                    		}else if((reqSocketReceiver.contentLength + reqSocketReceiver.contentStartIndex)>reqSocketReceiver.reqBytes.length){//扩大缓冲区
	                    			reqSocketReceiver.reqBytes = new byte[5120];//5K
	                    		}
	                		}
	                	}else{//后续写入
	                		reqSocketReceiver.saveFileStream.write(reqSocketReceiver.reqBytes, 0, reqSocketReceiver.receiveOffSet);
	                	}
	                	reqSocketReceiver.receiveOffSet = 0;//重置字节接收
	                }else{
	                	if (reqSocketReceiver.reqBytes.length < reqSocketReceiver.contentLength + reqSocketReceiver.contentStartIndex) {
	                        byte[] tmpUnionInfo = new byte[reqSocketReceiver.contentLength + reqSocketReceiver.contentStartIndex];//一次性分配内存
	                        System.arraycopy(reqSocketReceiver.reqBytes, 0, tmpUnionInfo, 0, reqSocketReceiver.receiveOffSet);
	                        reqSocketReceiver.reqBytes = null;
	                        reqSocketReceiver.reqBytes = tmpUnionInfo;
	                    }
	                }

	                int tmpToReadLen = reqSocketReceiver.contentLength + reqSocketReceiver.contentStartIndex - reqSocketReceiver.readedLength;
	                if (tmpToReadLen == 0) {//完成
	                	reqSocketReceiver.isCompleted = true;
	                	if(reqSocketReceiver.isSyncFile){//文件同步
	                		reqSocketReceiver.syncCode = "0";
	                		reqSocketReceiver.syncMsg = "保存至"+reqSocketReceiver.trgFilePath+"完成";
	                	}
	                	return reqSocketReceiver;
	                }else if(tmpToReadLen < 0){//结束
	                	reqSocketReceiver.syncCode = "-1";
	                	int expectLen = reqSocketReceiver.contentLength + reqSocketReceiver.contentStartIndex;
	                	int actualLen = reqSocketReceiver.readedLength-reqSocketReceiver.contentStartIndex;
	                	reqSocketReceiver.syncMsg = "实际接收总字节数"+reqSocketReceiver.readedLength+"与预期"+expectLen+"不符，其中实际接收内容字节数"+actualLen+"，预期内容字节数"+reqSocketReceiver.contentLength;
	                	reqSocketReceiver.hasError=true;
	                	reqSocketReceiver.errMsg=reqSocketReceiver.syncMsg;
	                	return reqSocketReceiver;
	                }
	            }else if (reqSocketReceiver.contentLength == 0){
	            	reqSocketReceiver.isCompleted = true;
	            	return reqSocketReceiver;
	            }
			}
		} catch(Throwable e){
			String tMsg = LogHelper.getStackTrace(e);
			if(!tMsg.contains("timed out") && !tMsg.contains("超时")){
				reqSocketReceiver.hasError = true;
				reqSocketReceiver.errMsg = LogHelper.getStackTraceFirstLine(e);
				FGBiz.limitMsg.exception("TenantSocket.receiveBytes", e);
			}
		}finally{
			if(reqSocketReceiver.saveFileStream!=null){
    			try {
					reqSocketReceiver.saveFileStream.close();
				} catch (IOException e) {
					FGBiz.limitMsg.exception("TenantSocket.receiveBytes", e);
				}
    			reqSocketReceiver.saveFileStream = null;
            }
		}
		return reqSocketReceiver;
    }
	
	public void disconnect(){
		if(socket != null){
			try {
				if(socket.isClosed()){
					socket.close();
				}
				socket = null;
			} catch (IOException e) {
				FGBiz.limitMsg.exception("TenantSocket.disconnect", e);
			}
		}
	}
}
